<?php // Util.php

class Util {
	
	public static function debug($label, $content=null, $verbose=false) {
		if ($verbose && !is_array($content) && $content != null) $content = array("DEBUG"=>$content);
		if ($verbose && $content != null) $content["STACK_TRACE"] = debug_backtrace();
		if ($content != null) $content = print_r($content, true);
		
		$dbg = "\n\n" . "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" ."\n";
		$dbg .= "\n"   . "                    {$label}                                " . "\n";
		if ($content != null) $dbg .= $content;
		
		$dbg .= "\n" . "------------------------------------------------------------" . "\n\n";
		error_log($dbg, 3, LOG_FILE); // write to LOG_FILE
	}

	public static function padNumber($num) {
		$n = str_pad($num, 2, '0', STR_PAD_LEFT);	
		return $n;	
	}
	
	public static function trimTo($str, $len) {
		if (strlen($str) > $len) {
			return substr($str, 0, $len-3) . "...";
		}
		return $str;
	}
	
	public static function checkEmail($email) {
		$email_matches = array();
		$found = preg_match('/^[a-zA-Z0-9.+_-]+@[a-zA-Z0-9._-]+\.[a-zA-Z]{2,3}$/', trim($email), $email_matches);

		if ($found === 0) return false;
		
		$email = str_replace("[", "", str_replace("]", "", $email_matches[0]));
		return $email;
	}
	
	public static function parseDateTime($s) {
		// expects '7/26/2011 11:26 AM' and returns a flat array: year, month, day, hour, min
		// if offset is specified, returns the date in GMT
		// TODO: develop logic around locales
		
		list($date, $time, $ampm) = explode(' ', $s);
		list($m, $d, $y) = explode('/', $date);
		list($h, $n) = explode(':', $time);
		if (strtolower($ampm) == 'pm' && $h < 12) $h += 12;
		$h += 0;
		
		$adj = Util::formatDateTime("{$y}-{$m}-{$d}T{$h}:{$n}:00");
		return $adj;
	}
	
	public static function formatDateTime($datetime) {
		// expects $datetime in YYYY-MM-DDTHH:MM:00 format
		// returns same format with UTC adjustments
		$user = new InsightUser();
		$dt = new DateTime($datetime, new DateTimeZone($user->timezone));
		$converted = $dt;
		
		$offset = $dt->getOffset();
		if ($offset != 0) {
			$interval = new DateInterval("PT".abs($offset)."S");
			if ($offset < 0) {
				$converted = $dt->add($interval);
			} else {
				$converted = $dt->sub($interval);
			}
		}
		
		return $converted->format("Y-m-d\TH:i:s.000\Z");
	}
	
	public static function formatDateTimeLong() {
		// returns format of "2011-08-26T18:00:00"
		$d = new DateTime();
		return $d->format("Y-m-d\TH:i:s.000\Z");
	}
	
	public static function encrypt($data) {
		if (PHP_VERSION >= '5.3.3') {
			return openssl_encrypt($data, 'AES-128-CBC', SFDC_OAUTH_CLIENT_ID, false, CRYPT_IV);
		} else {
			return openssl_encrypt($data, 'AES-128-CBC', SFDC_OAUTH_CLIENT_ID, false); 
		}
	}
	
	public static function decrypt($data) {
		if (PHP_VERSION >= '5.3.3') {
			return openssl_decrypt($data, 'AES-128-CBC', SFDC_OAUTH_CLIENT_ID, false, CRYPT_IV);
		} else {
			return openssl_decrypt($data, 'AES-128-CBC', SFDC_OAUTH_CLIENT_ID, false);
		}
	}
	
	public static function callout($endpoint, $method='GET', $headers=array(), $options=array(), $debug=false) {
		$c = curl_init();
		curl_setopt($c, CURLOPT_URL, $endpoint);
		array_push($headers, "Content-type: application/json");
		curl_setopt($c, CURLOPT_HTTPHEADER, $headers);
		curl_setopt($c, CURLINFO_HEADER_OUT, true);
		curl_setopt($c, CURLOPT_RETURNTRANSFER, true);
		curl_setopt($c, CURLOPT_FOLLOWLOCATION, true);
		curl_setopt($c, CURLOPT_HEADER, $debug);
		foreach ($options as $k => $v) {
			curl_setopt($c, $k, $v);
		}

		if ($method == 'GET')
			curl_setopt($c, CURLOPT_HTTPGET, true);
		else if ($method == 'POST')
			curl_setopt($c, CURLOPT_POST, true);
		
		$result = curl_exec($c);
		$response = new stdClass();
		$response->data = json_decode($result);
		
		$status_code = curl_getinfo($c, CURLINFO_HTTP_CODE);
		$response->status = $status_code;
		
		if ($debug) {
			Util::debug("request info",curl_getinfo($c));
			Util::debug("endpoint", $endpoint);
			Util::debug("callout result", $result);
		}

		curl_close($c);
		return $response;
	}
	
	public static function escape($string) {
		$esc_char = explode(' ', "? & | ! { } [ ] ( ) ^ ~ * : \\ \" ' + -");
		foreach ($esc_char as $e) $string = str_replace($e, "\\".$e, $string);
		return $string;
	}
	
	public static function cleanString($wrkString) {
		$wrkString = htmlspecialchars($wrkString,ENT_NOQUOTES);
		return trim($wrkString);
	}
	
	public static function getFld($obj, $field) {
		if (array_key_exists($field, $obj->fields)) {
			return $obj->fields->$field;
		}
		return null;
	}
	
	// Case insensitve version of in_array
	public static function in_iarray($needle, $haystack) {
		return in_array(strtolower($needle), array_map('strtolower', $haystack));
	}
	
	public static function array_index($haystack, $needle, $offset=0) {
		for ($i=$offset; $i < count($haystack); $i++) {
			if (strtolower($haystack[$i]) === strtolower($needle)) return $i;
		}
		return FALSE;
	}
	
	// Remove a value from an array and return the resulting array
	public static function array_remove($haystack, $needle, $offset=0) {
		$indx = Util::array_index($haystack, $needle, $offset);
		if ($indx !== FALSE) {
			if ($indx === 0) {
				array_shift($haystack); // removes the first element from the array, modifying the array
			} else if ($indx === count($haystack)-1) {
				array_pop($haystack); // removes the last element from the array, modifying the array
			} else {
				$a = array_slice($haystack, 0, $indx);
				$b = array_slice($haystack, $indx+1);
				$haystack = array_merge($a, $b);
			}
		}
		return $haystack;
	}
	
	public static function array_iunique($array) {
		return array_values(array_intersect_key(
			$array,
			array_unique(array_map('strtolower',$array))
		));
	}
	
	public static function cleanFields($sObj, $fields, $trim=true, $maintainBooleanStr=false) {
		$user = new InsightUser();

		if (!$user->hasObj($sObj)) {
			return array();
		} else {
			$accessible_fields = $user->getAccessibleFields($sObj);
			foreach ($fields as $k => $v) {
				if (!Util::in_iarray($k, $accessible_fields)) unset($fields[$k]);
				else $fields[$k] = Util::cleanString($v);
			}

			$pruned = Util::prune($fields, $trimString=$trim, $maintainBooleanString=$maintainBooleanStr);
			return $pruned;
		}
	}
	
	// removes null or empty values from an array
	public static function prune($ar, $trimString=true, $maintainBooleanString=false) {
		if (!$ar) return $ar;
		$obj = null;
		if (!is_object($ar) && !is_array($ar)) return $ar;
		if (is_object($ar)) $obj = new stdClass();
		else if (is_array($ar)) $obj = array();
		
		// turn off error reporting here; even for test
		$level = error_reporting();
		error_reporting(0);

		foreach ($ar as $k => $v) {
			if (gettype($v) == "object") {
				$pruned = self::prune($v);

				if (count($pruned)) {
					if (is_array($ar)) $obj[$k] = $pruned;
					else $obj->$k = $pruned;
				}
			
			} else if (is_array($v)) {
				if (is_array($obj)) {
					$obj[$k] = array();
					foreach ($v as $e => $f) {
						$obj[$k] = array_merge(
							$obj[$k], 
							array($e => self::prune($f))
						);
					}
				} else {
					$obj->$k = array();
					foreach ($v as $e => $f) {
						$obj->$k = array_merge(
							$obj->$k, 
							array($e => self::prune($f))
						);
					}
				}
			} else if ($maintainBooleanString==false && strtolower($v) == 'true') {
				if (is_array($ar)) $obj[$k] = true;
				else $obj->$k=true;

			} else if ($maintainBooleanString==false && strtolower($v) == 'false') {
				if (is_array($ar)) $obj[$k] = false;
				else $obj->$k=false;
			
			} else if (isset($v) && !(gettype($v) == 'string' && $v == '')) {
				if (gettype($v) == 'string' && $trimString) $v = substr($v, 0, 255); // trim text fields to 255 characters
				if (is_array($ar)) $obj[$k] = $v;
				else $obj->$k=$v;
			} 
		}
		
		error_reporting($level);
		return $obj;
	}
	
	public static function coalesce($v1, $v2) {
		if (self::isNullOrEmpty($v1)) return $v2;
		return $v1;
	}
	
	public static function isNullOrEmpty($s) {
		if (isset($s) && $s != '') return false;
		return true;
	}
	
	public static function logCase($subject, $body) {
		Util::debug($subject, $body);
		if (!INSIGHT_TEST) {
			$case = new SObject();
			$case->type = 'Case';
			$case->fields = array(
				'Subject' => $subject,
				'Description' => substr(htmlspecialchars(print_r($body, true)), 0, 31000),
				'Origin' => 'Application',
				'Request_URI__c' => $_SERVER["REQUEST_URI"]
			);
			
			// Clean up the session info before adding it
			$sess = $_SESSION;
			if (isset($sess["describe"])) {
				foreach ($sess["describe"] as $key => $field_def) {
					$sess["describe"][$key] = array_keys($field_def["fields"]);
				}
			}
			$sess["SESSION_ID"] = session_id();
			$case->fields["PHP_Session__c"] = substr(print_r($sess, true), 0, 31000);
			
			if (isset($_SESSION["user_id"])) {
				$case->fields['Salesforce_User_ID__c'] = $_SESSION["user_id"];				
			}
			
			Salesforce::$cirruspath = true;
			$conn = Salesforce::connect();
			try {
				$conn->create(array($case));
			} catch (Exception $e) {
				$lastRequest = array(
					"TIME" => date(DATE_RFC822),
					"STACK_TRACE" => $e,
					"HEADERS" => $conn->getLastRequestHeaders(),
					"REQUEST" => $conn->getLastRequest(),
					"RESPONSE" => $conn->getLastResponse()
				);
				Util::mailCI($subject, $lastRequest, false);
			}
		} else {
			Util::mailCI($subject, $body);
		}
	}
	
	public static function cleanSoapFaultException($ex) {
		if (strtolower(get_class($ex)) == 'soapfault') {
			$trace = $ex->getTrace();
			$new_ex = new stdClass();
				$new_ex->faultString = $ex->faultstring;
				$new_ex->faultCode = $ex->faultcode;
				$new_ex->trace = array_slice($trace, 0, 2);
			return $new_ex;
		} else {
			return $ex;
		}
	}
	
	public static function mailCI($subject, $body, $emailToCase=true) {
		// turn off error reporting here; even for test
		$level = error_reporting();
		error_reporting(0);
		
		try {
			if (is_object($body) || is_array($body)) {
				$body = print_r($body, true); 
			}
			
			// if we're in test mode, don't create a case
			if (INSIGHT_TEST) $emailToCase = false;
			
			$email = 'cirrus_insight_error_reports@42q7xr5izwi6ysjoc1p21f3tv.3pgfeeaa.3.apex.salesforce.com';
			if (!$emailToCase) $email = 'rhuff@cirruspath.com';

			mail($email, "[CI ERROR ".$_SESSION["user_id"]."] " . $subject, $body);
		} catch (Exception $e) {}
		
		error_reporting($level);
	}
	
	public static function setCustomLoginUrl($url, $erase=false) {
		$url = urldecode($url);
		if ($erase == false && (stripos($url, "https://") != 0 || stripos($url, ".my.salesforce.com") == -1)) {
			// invalid URL. Don't set the cookie
			return;
		}
		
		if (stripos($url, "/") != strlen($url)-1) $url .= "/";
		$expires = ($erase ? 
			time()+60*60*24*30*12 : // in one year 
			time()-(3600 * 25) // erase it
		);
		$value = ($erase ? 
			"" : // erase 
			$url // set it
		);
		
		setcookie(
			$name="custom_login_url", 
			$data=$value, 
			$expire=$expires,
			$path="/", 
			$domain=SESSION_HOST, 
			$secure=true, // Only set this cookie when the script is accessed over HTTPS 
			$httponly=true // The cookie will be made accessible only through the HTTP protocol. This means that the cookie won't be accessible by scripting languages, such as JavaScript
		);
	}
	
	public static function logout($killToken=false) {
		// Unset all of the session variables.
		$_SESSION = array();
		
		if (ini_get("session.use_cookies")) {
			$params = session_get_cookie_params();
			setcookie(session_name(), '', time() - 42000,
				$params["path"], $params["domain"],
				$params["secure"], $params["httponly"]
			);	    
		}
	
		if ($killToken) {
			// kill the auto-login cookie as well
			setcookie(
				$name="insight_token", $data="", // basics 
				$expire=time() - 42000,
				$path="/", 
				$domain=SESSION_HOST, 
				$secure=true,
				$httponly=true
			);
		}
		
		// Finally, destroy the session.
		session_destroy();
	}
	
	// remove null byte and quotes from $_REQUEST param values
	public static function cleanRequest() {
		foreach ($_REQUEST as $k => $v) {
			$_REQUEST[$k] = str_replace("%00", '', $v);
			$_REQUEST[$k] = str_replace("\"", "", $v);
			$_REQUEST[$k] = str_replace("'", "", $v);
			$_REQUEST[$k] = str_replace("<", "", $v);
			$_REQUEST[$k] = str_replace(">", "", $v);
			$_REQUEST[$k] = str_replace("(", "", $v);
			$_REQUEST[$k] = str_replace(")", "", $v);
			$_REQUEST[$k] = str_replace("*", "", $v);
		}
	}

	public static function cacheWSDL($url, $filename, $sessionid) {
		$ch = curl_init();
			$fp = fopen($filename, "w");
			curl_setopt($ch, CURLOPT_URL, $url);
			curl_setopt($ch, CURLOPT_FILE, $fp);
			curl_setopt($ch, CURLOPT_HEADER, 0);
			curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
			curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
			curl_setopt($ch, CURLOPT_COOKIE, 'sid='.$sessionid);
			setcookie("sid", $sessionid, 0, "/", ".salesforce.com", 0);
			curl_setopt($ch, CURLOPT_TIMEOUT, 30);
			curl_exec($ch);
			fclose($fp);
		curl_close($ch);	
	}
	
	public static function setNewPeople($new_people) {
		setcookie(
			$name = "new_people",
			$value = json_encode($new_people),
			$expire = time()+60*3, // 3 minutes
			$path = "/",
			$domain = SESSION_HOST,
			$secure = true,
			$httponly = true  
		);
	}
	
	function endsWith( $str, $sub ) {
		return ( substr( $str, strlen( $str ) - strlen( $sub ) ) === $sub );
	}
}

?>